﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using LiteDB;

namespace MyFound
{
   public class Program
    {
        private static void Main(string[] args)
        {
            UpdateFoundEquity();

            #region 添加交易记录
            var trades = new List<Trade>();
            //#region 164304
            //Trade trade = CreateTradeForFee(1.3630m, 5000, 29.82m, true);
            //trade.FdCode = "164304";
            //trade.DateTime = new DateTime(2015, 11, 13);
            //trades.Add(trade);

            //trade = CreateTradeForFee(1.340m, 300, 1.79m, true);
            //trade.FdCode = "164304";
            //trade.DateTime = new DateTime(2015, 12, 02);
            //trades.Add(trade);

            //trade = CreateTradeForFee(1.3390m, 300, 1.79m, true);
            //trade.FdCode = "164304";
            //trade.DateTime = new DateTime(2015, 12, 16);
            //trades.Add(trade);

            //trade = CreateTradeForFee(1.378m, 300, 1.79m, true);
            //trade.FdCode = "164304";
            //trade.DateTime = new DateTime(2015, 12, 30);
            //trades.Add(trade);

            //Spilit(trades, 1.007720350m);


            //trade = CreateTradeForFee(1.071m, 100, 0.6m, true);
            //trade.FdCode = "164304";
            //trade.DateTime = new DateTime(2016, 2, 17);
            //trades.Add(trade);

            //trade = CreateTradeForFee(1.108m, 100, 0.6m, true);
            //trade.FdCode = "164304";
            //trade.DateTime = new DateTime(2016, 2, 24);
            //trades.Add(trade);


            //#endregion

            //#region 690009、001417
            //trade = CreateTradeForFee(1.797m, 10000m, 59.64m, true);
            //trade.FdCode = "690009";
            //trade.DateTime = new DateTime(2015, 11, 13);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.801m, 10000m, 0m, true);
            //trade.FdCode = "001417";
            //trade.DateTime = new DateTime(2015, 6, 16);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.9580m, 500m, 2.98m, true);
            //trade.FdCode = "001417";
            //trade.DateTime = new DateTime(2015, 11, 19);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.98m, 500m, 2.98m, true);
            //trade.FdCode = "001417";
            //trade.DateTime = new DateTime(2015, 11, 25);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.96m, 500m, 2.98m, true);
            //trade.FdCode = "001417";
            //trade.DateTime = new DateTime(2015, 12, 09);
            //trades.Add(trade);

            //trade = CreateTradeForFee(1m, 10000m, 111.81999999999971m, true);
            //trade.FdCode = "001637";
            //trade.DateTime = new DateTime(2015, 12, 03);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.9660m, 100m, 0.6m, true);
            //trade.FdCode = "001637";
            //trade.DateTime = new DateTime(2016, 5, 09);
            //trades.Add(trade);

            //#endregion

            //Trade trade = CreateTradeForFee(0.8930m, 4911, 29.29m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2015, 7, 22);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.6630m, 500, 2.98m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2015, 9, 26);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.9120m, 300, 1.79m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2015, 11, 25);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.8640m, 500, 2.98m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2015, 12, 2);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.8510m, 1000, 5.96m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2015, 12, 3);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.8610m, 300, 1.79m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2015, 12, 09);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.6610m, 1000, 5.96m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2016, 2, 26);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.6120m, 100, 0.6m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2016, 3, 1);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.7220m, 100, 0.6m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2016, 4, 13);
            //trades.Add(trade);

            //trade = CreateTradeForFee(0.7260m, 100, 0.6m, true);
            //trade.FdCode = "001158";
            //trade.DateTime = new DateTime(2016, 4, 19);
            //trades.Add(trade);
            //SaveTrades(trades);

            //SaleByFee("001158", 754m, 0.8050m, 3.03m,new DateTime(2015,10,20));
            #endregion


            while (true)
            {
                Console.Title = DateTime.Now.ToString();
                OutputInfo();
                System.Threading.Thread.Sleep(3000);
            }

            Console.ReadLine();
        }

        public static void SaleByFee(string fdCode, decimal count, decimal equity, decimal fee, DateTime dateTime)
        {
            Sale(fdCode, count, equity, fee / (count * equity), dateTime);
        }

        public static void Sale(string fdCode, decimal count, decimal equity, decimal rate, DateTime dateTime)
        {
            var liteDatabase = new LiteDatabase("lite.db");
            liteDatabase.BeginTrans();
            var tradeCol = liteDatabase.GetCollection<Trade>("Trade");
            var orderCol = liteDatabase.GetCollection<Order>("Order");
            var trades = tradeCol.Find(x => x.FdCode == fdCode && x.RealCount > 0 && x.DateTime <= dateTime).OrderBy(x => x.RealEquity).ThenBy(x => x.DateTime).ToList();
            if (trades.Any() && trades.Sum(x => x.RealCount) >= count)
            {
                trades.ForEach(trade =>
                {
                    if (count > 0)
                    {
                        var order = new Order();
                        order.TradeId = trade.Id;
                        if (trade.RealCount >= count)
                        {
                            order.Count = count;
                            trade.RealCount -= count;
                            count = 0;
                        }
                        else
                        {
                            order.Count = trade.RealCount;
                            count -= trade.RealCount;
                            trade.RealCount = 0;

                        }
                        order.DateTime = dateTime;
                        order.Equity = equity;
                        order.Fee = (order.Count * order.Equity) * rate;
                        order.Rate = rate;

                        tradeCol.Update(trade);
                        orderCol.Insert(order);
                    }
                });
            }
            liteDatabase.Commit();
        }



        public static void OutputInfo()
        {
            var liteDatabase = new LiteDatabase("lite.db");
            var foundCol = liteDatabase.GetCollection<Found>("Found");
            var founds = foundCol.FindAll();
            var tradeCol = liteDatabase.GetCollection<Trade>("Trade");
            var trades = tradeCol.Find(x => x.RealCount > 0);

            trades.ToList().ForEach(trade =>
            {
                var found = founds.First(f => f.Code == trade.FdCode);
                found.GrEquity = FoundApi.GetGrEquity(found.Code);
                foundCol.Update(found);
            });

            Console.Clear();
            trades.OrderBy(x => x.FdCode).ToList().ForEach(trade =>
            {
                var found = founds.First(f => f.Code == trade.FdCode);

                Console.CursorVisible = false;

                var syl = (found.Equity - trade.RealEquity) / found.Equity;
                var gssyl = found.GrEquity > 0 ? (found.GrEquity - trade.RealEquity) / found.GrEquity : 0;

                Console.ForegroundColor = ConsoleColor.White;
                Console.Write("{0}\t份{1,10}\t净{2,4:C}({3,4:C})", trade.FdCode, Math.Round(trade.RealCount, 2), Math.Round(trade.RealEquity, 2), found.GrEquity);

                Console.Write("\t收");
                Console.ForegroundColor = syl > 0 ? ConsoleColor.Red : ConsoleColor.Green;
                Console.Write("{0,10:C}", syl * trade.RealCount * trade.RealEquity);
                Console.ForegroundColor = ConsoleColor.White;

                Console.Write("\t率");
                Console.ForegroundColor = syl > 0 ? ConsoleColor.Red : ConsoleColor.Green;
                Console.Write("{0,10:P}", syl);
                Console.ForegroundColor = ConsoleColor.White;

                Console.Write("\t估");
                Console.ForegroundColor = gssyl > 0 ? ConsoleColor.Red : ConsoleColor.Green;
                Console.Write("{0,10:C}", gssyl * trade.RealCount * trade.RealEquity);
                Console.ForegroundColor = ConsoleColor.White;

                Console.Write("\t率");

                Console.ForegroundColor = gssyl > 0 ? ConsoleColor.Red : ConsoleColor.Green;

                Console.Write("{0,10:P}", gssyl);
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write(Environment.NewLine);

                //Console.WriteLine("{0}\t数量{2}\t成本{4:C}\t收益率{1:P}\t估算收益率{5:P}\t收益{3:C}", trade.FdCode, (found.Equity - trade.RealEquity) / found.Equity, trade.RealCount, (found.Equity - trade.RealEquity) / found.Equity*(trade.Principal- (trade.FeeType==0? trade.Fee:0)),
                //    trade.RealCount*trade.RealEquity+trade.Fee,
                //    found.GrEquity>0?(found.GrEquity - trade.RealEquity) / found.GrEquity:0);
            });
            Console.WriteLine("".PadLeft(Console.BufferWidth,'-'));
            trades.GroupBy(x => x.FdCode).ToList().ForEach(x => Console.WriteLine("{0}\t份{1,10}\t本{2,10:C}", x.Key, Math.Round(x.Sum(y => y.RealCount), 2), Math.Round(x.Sum(y => y.RealEquity * y.RealCount), 2)));
            Console.WriteLine("".PadLeft(Console.BufferWidth, '-'));
            Console.WriteLine("{0}\t份{1,10}\t本{2,10:C}\t估{3,10:C}", "合计", Math.Round(trades.Sum(y => y.RealCount), 2), Math.Round(trades.Sum(y => y.RealEquity * y.RealCount), 2), Math.Round(trades.Sum(y => y.RealCount * founds.First(f => f.Code == y.FdCode).GrEquity), 2));
        }

        public static void SaveTrades(IEnumerable<Trade> trades)
        {
            if (trades != null && trades.Any())
            {
                var liteDatabase = new LiteDatabase("lite.db");
                var tradeCol = liteDatabase.GetCollection<Trade>("Trade");
                trades.ToList().ForEach(t =>
                {
                    var storeTrade = tradeCol.FindOne(x => x.DateTime == t.DateTime && x.FdCode == t.FdCode);
                    if (storeTrade != null)
                    {
                        t.Id = storeTrade.Id;
                        tradeCol.Update(t);
                    }
                    else
                    {
                        tradeCol.Insert(t);
                    }
                });
                liteDatabase.Commit();
            }


        }

        public static void Spilit(IEnumerable<Trade> trades, decimal rate)
        {
            trades.ToList().ForEach(t =>
            {
                t.RealCount = t.RealCount * rate;
                t.RealEquity = t.RealEquity / rate;
            });
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="equity">净值</param>
        /// <param name="principal">成本</param>
        /// <param name="rate">费率</param>
        /// <param name="isFront">前端或后端</param>
        /// <returns></returns>
        public static Trade CreateTrade(decimal equity, decimal principal, decimal rate, bool isFront)
        {
            var trade = new Trade();
            if (isFront)
            {
                trade.Count = principal * (100 - rate) / 100 / equity;
                trade.RealCount = trade.Count;
                trade.Equity = equity;
                trade.RealEquity = trade.Equity;
                trade.Fee = principal * rate / 100;
                trade.Principal = principal;
                trade.Rate = rate;

            }
            else
            {
                trade.Count = principal / equity;
                trade.RealCount = trade.Count;
                trade.Equity = equity;
                trade.RealEquity = trade.Equity;
                trade.Fee = principal * rate / 100;
                trade.Principal = principal;
                trade.Rate = rate;
            }
            return trade;
        }

        public static Trade CreateTradeForFee(decimal equity, decimal principal, decimal fee, bool isFront)
        {
            return CreateTrade(equity, principal, fee / principal * 100, isFront);
        }

        public static void UpdateFoundEquity()
        {

            var liteDatabase = new LiteDatabase("lite.db");
            var founds = liteDatabase.GetCollection<Found>("Found");
            var foundCodes = founds.FindAll().Select(x => x.Code).ToList();   //new[] { "164304", "001158", "001417", "690009" }
            Dictionary<string, Class1> founDictionary = FoundApi.ListFound(foundCodes);
            foreach (var kv in founDictionary)
            {
                if (!founds.Exists(f => f.Code == kv.Key))
                {
                    founds.Insert(new Found()
                    {
                        Code = kv.Key,
                        Equity = kv.Value.Nav,
                        Name = kv.Value.Name
                    });
                }
                else
                {
                    var found = founds.FindById(kv.Key);
                    found.Equity = kv.Value.Nav;
                    found.Name = kv.Value.Name;
                    founds.Update(found);
                }
            }
            liteDatabase.Commit();
        }

       public static void AddFound(string code)
       {
           var liteDatabase = new LiteDatabase("lite.db");
           var founds = liteDatabase.GetCollection<Found>("Found");
           if (!founds.Exists(x => x.Code == code))
           {
               founds.Insert(new Found {Code = code});
           }
       }

       public static IEnumerable<Found> ListFound()
       {
           var liteDatabase = new LiteDatabase("lite.db");
           var founds = liteDatabase.GetCollection<Found>("Found");
           return founds.FindAll().ToList();
       }
    }
}
